<?php

// Upload 上传类
// TODO: ftp, 云: 存储?

class Upload
{
    private $field; // 文件域名
    private $config = [
        "size" => 500, //单位KB
        "exts" => [".gif", ".png",'.jpg'], 
        //'path' = > '', // 'mod/yyyy-md/doc-id',
    ]; 
    private $mode = ''; 
    private $path = ''; 
    private $res1 = []; // 一个上传状态信息,

    private $oriName; // 原始文件名
    private $fileSize; // 文件大小
    private $fileName; // 新文件名
    private $fileExt; // 文件类型
    private $saveName; // 保存文件名
    private $viewPath; // 查看文件名

    /**
     * @param string $field 表单名称
     * @param array $config 配置项
     * @param mode upload/remote/base64: 为空不默认执行
     */
    function __construct($field='file', $config=[], $mode="comm"){
        $this->field = $field;
        if(!empty($config)){
            $this->config = $config + $this->config; //dump($this->config);
        }
        if($mode=="remote"){
            $this->saveRemote();
        }elseif($mode=="base64"){
            $this->upBase64();
        }elseif($mode){ // comm
            $this->upFile();
        }
    }

    // 普通上传文件
    function upFile($field=''){
        $this->init('comm');
        $field = $field ? $field : $this->field;
        if (empty($_FILES[$field])) {
            return $this->setState("Empty SourceFile");
        }
        $file = $_FILES[$field];
        if ($file['error']) {
            return $this->setState($file['error']);
        } else if (!file_exists($file['tmp_name'])) {
            return $this->setState("TmpFile NotFound");
        } else if (!is_uploaded_file($file['tmp_name'])) {
            return $this->setState("Error TmpFile");
        }

        $this->oriName = $file['name'];
        $this->fileSize = $file['size'];
        $this->getNames();
        $this->setNames();

        //检查文件大小是否超出限制
        if (!$this->checkSize()){
            return $this->setState("FileSize Exceed");
        }
        //检查是否不允许的文件格式
        if (!$this->checkType()) {
            return $this->setState("Error FileType");
        }
        //移动文件
        $this->saveFile($file);
    }
    // base64编码的图片上传
    function upBase64($imgData=''){
        $this->init('base64');
        $base64Data = $imgData ? $imgData : $_POST[$this->field];
        $img = base64_decode($base64Data);

        $this->fileSize = strlen($img);
        $this->oriName = 'base64_'.$this->fileSize.'_'.Basic::kidTemp('5.5');
        $this->getNames();
        $this->setNames();

        //检查文件大小是否超出限制
        if (!$this->checkSize()) {
            return $this->setState("FileSize Exceed");
        }
        //移动文件
        $this->saveFile($img);
    }
    // 拉取远程图片
    function saveRemote($imgUrl=''){
        $this->init('remote');
        $imgUrl = $imgUrl ? $imgUrl : $this->field;
        $imgUrl = htmlspecialchars($imgUrl);
        $imgUrl = str_replace("&amp;", "&", $imgUrl); 

        //http开头验证
        if (strpos($imgUrl, "http") !== 0) {
            return $this->setState("Error HttpLink");
        }
        //获取请求头并检测死链
        $heads = get_headers($imgUrl);
        if (!(stristr($heads[0], "200") && stristr($heads[0], "OK"))) {
            return $this->setState("Error DeadLink");
        }
        // Content-Type验证
        if (@stristr($heads['Content-Type'], "image")) { 
            return $this->setState("Error HttpContentType");

        }
        //打开输出缓冲区并获取远程图片
        ob_start();
        $context = stream_context_create(
            array('http' => array(
                'follow_location' => false // don't follow redirects
            ))
        );
        readfile($imgUrl, false, $context);
        $img = ob_get_contents();
        ob_end_clean();
        $pinfo = parse_url($imgUrl); 

        $this->oriName = isset($pinfo['path']) ? basename($pinfo['path']) : basename($imgUrl);
        $this->fileSize = strlen($img);
        $this->getNames();
        $this->setNames();

        //检查文件大小是否超出限制
        if (!$this->checkSize()) {
            return $this->setState("FileSize Exceed");
        }
        //移动文件
        $this->saveFile($img);
    }

    // 保存-可重写
    function saveFile($file){
        if($this->mode=='comm'){
            if (!move_uploaded_file($file["tmp_name"], $this->saveName)) {
                return $this->setState("MoveFile Error");
            }
        }else{
            if (!(Fsop::put($this->saveName, $file))) { //移动失败
                return $this->setState("Error WriteContent");
            } 
        }
        $this->upEnd();
    }
    // 上传End-可重写
    private function upEnd(){
        if(in_array($this->fileExt,array('.jpg','.jpeg'))){
            //Image::compress($this->saveName);
            //$res[] = compress::compcut($full, $max);
        }
    }

    private function getNames(){
        $ext = Fsop::extName($this->oriName); if(empty($ext)){ $ext = '.jpg'; } 
        $this->fileExt = $ext;
        if(empty($this->config['path'])){
            $path = 'comup/' . date('Y-m'); 
            $this->path = "/uptemp/$path";
        }else{
            $path = $this->config['path'];
            $this->path = $path;
        }
        Fsop::chkDirs($path, '1', 0); // str_replace()
        $upren = uval('upname','auto');
        if($upren=='auto'){
            $org = Basic::kidTemp('5.5');
        }else{
            if(in_array($this->mode,array('remote','base64'))){
                $org = $this->oriName;
            }else {
                $org = str_replace($ext, '', $this->oriName);    
            }
            $org = str_replace(["'",'#','&','%','+'], '~', $org);
        }
        $this->fileName = strtolower($org.'-'.Basic::kidRand('f',5).$ext);
    }
    private function setNames(){
        $this->saveName = DIR_DATA . $this->path . "/$this->fileName";
        $this->viewPath = PATH_DATA . $this->path . "/$this->fileName";
    }

    // 文件类型检测
    private function checkType(){
        $ext = $this->fileExt;
        // /name.php+ 1.jpg -> |asa|cdx|cer|php2|php3|php4
        // /name.jpg?x-oss-process=image/resize,m_fill,h_160,w_120;
        if(preg_match("/\.(asp|aspx|jsp|php|exe|sh|bat|com)$/",$this->oriName)){
            return false;
        }
        $flag = $this->config["exts"]==='(supper)' ? true : in_array($ext, $this->config["exts"]);
        return $flag;
    }
    // 文件大小检测
    private function checkSize(){
        $flag = $this->config["size"]==='(supper)' ? true : $this->fileSize <= ($this->config["size"]*1024);
        return $flag;
    }

    // 可能多次调用，每次调用重置信息
    function init($mode){
        $this->mode = $mode;
        $this->res1 = ['errno'=>0, 'errmsg'=>''];
        $this->path = '';
        $this->oriName = '';
        $this->fileSize = 0; 
        $this->fileName = '';
        $this->fileExt = '';
        $this->saveName = '';
        $this->viewPath = '';
    }
    // 上传错误信息
    private function setState($errmsg){
        $this->res1 = ['errno'=>612, 'errmsg'=>$errmsg];
    }
    // 上传结果信息
    function getRes(){
        $rec = $this->res1;
        $rec['mode'] = $this->mode;
        $rec['org'] = $this->oriName;
        $rec['size'] = $this->fileSize;
        $rec['ext'] = $this->fileExt;
        $rec['name'] = $this->fileName;
        $rec['path'] = $this->path; // 相对路径 mod/yyyy-md/doc-id
        $rec['save'] = $this->saveName; // 保存路径
        $rec['view'] = $this->viewPath; // 查看url
        return $rec;
    }

}
